/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package daemonClient;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.rmi.UnexpectedException;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class Client
{
	private String _ip = "127.0.0.1";
	private int _port = 2106;
	private Socket _socket;
	protected Shell shell;
	private PrintStream out = null;
	private Reader reader = null;
	private Button connectButton, disconnectButton = null;
	private static Text display = null;
	private Text input;
	private Button button;

	public static void main(String[] args)
	{
		try
		{
			Client window = new Client();
			window.open();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}

	}

	public void open()
	{
		Display display = Display.getDefault();
		createContents();
		shell.open();
		shell.layout();
		while (!shell.isDisposed())
		{
			if (!display.readAndDispatch())
			{
				display.sleep();
			}
		}
	}

	/**
	 * Create contents of the window.
	 */
	protected void createContents()
	{
		shell = new Shell(SWT.SHELL_TRIM & ~SWT.RESIZE & ~SWT.MAX);
		shell.setSize(450, 300);
		shell.setText("Client");

		connectButton = new Button(shell, SWT.NONE);
		connectButton.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				try
				{
					_socket = new Socket(_ip, _port);
					out = new PrintStream(_socket.getOutputStream());
					input.setEditable(true);
					reader = new Reader();
					reader.start();
					setVisibilityForAllExceptConnectButton(true);
					display.append("Connected.\n");
				}
				catch (Exception ee)
				{
					if (out != null)
						out.close();
					if (ee.getMessage().contains("Connection refused: connect"))
						display.append("Server is not online.\n");
					else
						display.append(ee.getMessage() + "\n");
				}
			}
		});
		connectButton.setBounds(1, 235, 68, 23);
		connectButton.setText("Connect");

		button = new Button(shell, SWT.NONE);
		button.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				try
				{
					out.println(Sifrovani.encrypt(input.getText()));
				}
				catch (UnexpectedException e1)
				{
					e1.printStackTrace();
				}
				input.setText("");
			}
		});
		button.setText("Send");
		button.setBounds(188, 235, 68, 23);
		button.setEnabled(false);

		disconnectButton = new Button(shell, SWT.NONE);
		disconnectButton.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				try
				{
					out.println("ClientControlShutdown");
					_socket.close();
					out.close();
					reader.interrupt();
					display.append("Disconnected\n");
					input.setEditable(false);
					setVisibilityForAllExceptConnectButton(false);
				}
				catch (Exception e1)
				{
				}
			}
		});
		disconnectButton.setBounds(366, 235, 68, 23);
		disconnectButton.setText("Disconnect");
		disconnectButton.setEnabled(false);

		input = new Text(shell, SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);
		input.setEditable(false);
		input.setBounds(1, 107, 442, 100);

		/*
		 * input.addKeyListener(new KeyListener() {
		 * 
		 * @Override public void keyReleased(KeyEvent arg0) { // System.out.println(arg0.keyCode);
		 * if (arg0.keyCode == 13 || arg0.keyCode == 16777296) // Velky a maly enter {
		 * out.println(input.getText()); input.setText(""); } }
		 * 
		 * @Override public void keyPressed(KeyEvent arg0) {
		 * 
		 * } });
		 */

		display = new Text(shell, SWT.WRAP | SWT.BORDER | SWT.VERTICAL);
		display.setBounds(1, 1, 442, 100);
		display.setEditable(false);
	}

	private void setVisibilityForAllExceptConnectButton(boolean b)
	{
		connectButton.setEnabled(!b);
		disconnectButton.setEnabled(b);
		button.setEnabled(b);
	}

	class Reader extends Thread
	{
		private BufferedReader in = null;

		public void run()
		{
			try
			{
				in = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}

			// if (!Sifrovani.isPhraseSet())
			getPhrase();

			while (true)
			{
				try
				{
					final String msg = in.readLine();

					if (msg != null)
					{

						Display.getDefault().asyncExec(new Runnable()
						{
							@Override
							public void run()
							{
								display.append(msg + "\n");
							}
						});
					}
					Thread.sleep(50);
				}
				catch (IOException e)
				{
					this.interrupt();
				}
				catch (InterruptedException e)
				{
				}
			}
		}

		private void getPhrase()
		{
			try
			{
				BufferedOutputStream bout = new BufferedOutputStream(_socket.getOutputStream());
				BufferedInputStream bin = new BufferedInputStream(_socket.getInputStream());

				Thread.sleep(100);
				// 1st pass
				String data = readData(bin);
				Thread.sleep(100);
				Vernam v = new Vernam();
				v.setPhrase(data);
				v.generateKey();
				writeData(v.XOR(), bout);
				Thread.sleep(100);
				// 2nd pass
				String s = readData(bin);
				Thread.sleep(100);
				v.setPhrase(s);
				Sifrovani.setPhrase(new String(v.XOR()));
				v.forget();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		private void writeData(byte[] xor, BufferedOutputStream bout) throws IOException
		{
			int len = xor.length + 2;
			bout.write(len & 0xff);
			bout.write(len >> 8 & 0xff);
			bout.write(xor);
			bout.flush();

		}

		private String readData(BufferedInputStream bin) throws IOException
		{
			int lengthLo = bin.read();
			int lengthHi = bin.read();
			int length = lengthHi * 256 + lengthLo;

			byte[] data = new byte[length - 2];

			int receivedBytes = 0;
			int newBytes = 0;
			int left = length - 2;
			while (newBytes != -1 && receivedBytes < length - 2)
			{
				newBytes = bin.read(data, receivedBytes, left);
				receivedBytes = receivedBytes + newBytes;
				left -= newBytes;
			}

			return new String(data);
		}

	}
}